
package ec.edu.ups.cidi.siia.plamdep.ejecutor;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ec.edu.ups.cidi.siia.plamdep.analizadorLexico.analisis.AnalisisUtils;
import ec.edu.ups.cidi.siia.plamdep.cargadocumentos.DocumentoLeido;
import ec.edu.ups.cidi.siia.plamdep.configuracion.ConfiguracionConexionWeb;
import ec.edu.ups.cidi.siia.plamdep.configuracion.ConfiguracionGlobal;
import ec.edu.ups.cidi.siia.plamdep.ejecutor.GestionAlgoritmos.AlgComparacionContainer;
import ec.edu.ups.cidi.siia.plamdep.ejecutor.GestionAlgoritmos.AlgExtractorContainer;
import ec.edu.ups.cidi.siia.plamdep.ejecutor.GestionAlgoritmos.CargaJAR;
import ec.edu.ups.cidi.siia.plamdep.ejecutor.GestionAlgoritmos.GestorAlgoritmos;
import ec.edu.ups.cidi.siia.plamdep.modelo.algortimos.*;
import ec.edu.ups.cidi.siia.plamdep.modelo.reporte.ReporteData;
import ec.edu.ups.cidi.siia.plamdep.modelo.webcrawler.Bibliografia;
import ec.edu.ups.cidi.siia.plamdep.modelo.webcrawler.Pagina;
import ec.edu.ups.cidi.siia.plamdep.utils.Log;
import ec.edu.ups.cidi.siia.plamdep.utils.ReportesManager;
import ec.edu.ups.cidi.siia.plamdep.utils.*;
import ec.edu.ups.cidi.siia.plamdep.utils.hilos.ProcesoGestionado;
import ec.edu.ups.cidi.siia.plamdep.utils.hilos.ProcessPool;

public class Ejecutor{
    
    private static CargaJAR loader=new CargaJAR();
    static Ejecutor instanciaEjecutor;
    
    
    
    private static ArrayList<Fuente> fuentes=new ArrayList<Fuente>();
    /**
     * Arranca los servicios y carga los algoritmos con los que trabajara
     * @param algscom
     * @param algsex
     */
    public Ejecutor( List<AlgComparacionContainer> algscom, List<AlgExtractorContainer> algsex )
    {
    	instanciaEjecutor=this;
    	/**Carga la configuracion de la plataforma*/
    	ConfiguracionGlobal.cargarConfiguracion();
    	/** arranca los servicios*/
        Servicios.arrancarAnalizadorLexico();
        Servicios.arrancarBuscadorWeb();
        cargarAlgoritmos(algscom, algsex);
    	
    	
    	
    }
    public static void cargarAlgoritmos(List<AlgComparacionContainer> algscom, List<AlgExtractorContainer> algsex)
    {
    	GestorAlgoritmos.quitarAlgoritmos();
    	/** carga algortimos de comparacion */
    	for(AlgComparacionContainer alg:algscom)
    		 loader.loadAlgoritmoComparacion(alg);
    		
    	
    	/** carga algortimos de comparacion */
    	for(AlgExtractorContainer alg: algsex)
    		loader.loadAlgoritmoExtraccion(alg);
    }
    /**
     * Metodo que realiza el proceso de analisis
     * @param docAnalizarPath
     * @param algComparacion
     * @param algExtraccion
     * @param buscarInternet
     * @param fuentesLocales
     * @return
     */
    public ResultAnalisis analizar(String docAnalizarPath,String algComparacion, String algExtraccion, boolean buscarInternet, List<String> fuentesLocales,boolean descargarDocumento, int nivelFrecuencia)
    {
    	ResultAnalisis analisis=new ResultAnalisis();
    	//Fase 1: Carga el documento
    	DocumentoLeido docAnalizar=new DocumentoLeido(docAnalizarPath);
    	//List<Fuente> fuentes=new ArrayList<Fuente>();
    	List<ResultAnalisisFinal> resultadosComparador=new ArrayList();
    	List<ResultAnalisisFinal> resultadosExtractor=new ArrayList();
    	String contenidoSospechoso=docAnalizar.getContenido();
    	docAnalizar=null;
    	//Utils.garbageCleaner();
    	//Fase 2 Busca y Carga las posibles fuentes 
    	///////////////////////////////////////////////////////
        if(buscarInternet)//carga las posibles fuentes de internet
           fuentes=this.buscarFuentesInternet(algExtraccion, contenidoSospechoso,descargarDocumento,nivelFrecuencia);
       
        List<Fuente> locales=this.buscarFuentesLocales(fuentesLocales);//carga las posibles fuentes locales
        for(Fuente f:locales) fuentes.add(f);
      
        //Fase 3 Ejecuta el algoritmo de deteccion por cada fuente
        ////////////
        AlgoritmoComparacion alComparacion= GestorAlgoritmos.getAlgoritmoComparacion(algComparacion);
      
    	Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"Inciando comparacion con:"+fuentes.size()); 
        for(Fuente f:fuentes)
        if(f!=null)
        {
        	Utils.garbageCleaner();
        	
        	ResultAnalisisFinal rextract=new ResultAnalisisFinal();
        	rextract.setFuente(f);
        	rextract.setPlagioDocPorcentaje(0.d);
        	
        	resultadosExtractor.add(rextract);
        	
        	
        	
        	Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"Comparando con documento:"+f.getRutaOrigen()); 
	        alComparacion.setFuente(f);
	        alComparacion.setTextoAnalizar(contenidoSospechoso);
	        alComparacion.ejecutar();
	        ResultAnalisisFinal result= alComparacion.getResultadoFinal();
		    result.setFuente(f);
		    resultadosComparador.add(result.clone());
        }
        
        //limpia la cache
        AnalisisUtils.limpiarCache();
        Utils.garbageCleaner();
        fuentes=new ArrayList<Fuente>();
        
        analisis.setResultadosComparacion(resultadosComparador);
        analisis.setResultadosExtraccion(resultadosExtractor);
        
        return analisis;
    }
    
    /**
     * Metodo que 
     * @param algExtraccion
     * @param contenido
     * @param descargarDocumento
     * @return
     */
    private   ArrayList<Fuente> buscarFuentesInternet(String algExtraccion, String contenido, boolean descargarDocumento,int nivelFrecuencia)
    {
    	try{
    	Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"**********************Ejecutando algoritmo de extraccion:"+algExtraccion);
    	fuentes=new ArrayList<Fuente>();
        AlgoritmoExtraccion extractor= GestorAlgoritmos.getAlgoritmoExtraccion(algExtraccion);
        if(extractor!= null)
        { 
        		 //Ejecucion de extractor
        		 List<Keyword> keywords=null;
        		 try
        		 {
			         extractor.setTextoToAnalizar(contenido);
			         extractor.ejecutar();
			         keywords=extractor.getPalabrasClave();
        		 }catch(Exception erroExtraccion)
        		 {
        			 keywords=new ArrayList<Keyword>();
        			 Log.log(erroExtraccion,Log.MOD_EJECUTOR,Log.LEVEL_ERROR, Ejecutor.class,"Error al ejecutar extraccion");
        		 }
		         ////////////////////////////////
        		 Utils.garbageCleaner();

		         
 		     	 Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"**********************Finaliza extraccion de keywords total:"+keywords.size());
		         
		         
		         Map<String,DescargasManager>descargasMap=new HashMap<String,DescargasManager>();
		        
		         
		         int kIdx=1;
		         int totK=keywords.size();
		         for(Keyword key:keywords) //prepara las conexiones para todas las descargas
		         {
		            try{
		            Utils.garbageCleaner();
		        	Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"\n\n\n**********************Iniciando buqueda "+kIdx+" de "+totK+" con motores para: "+key.getKeyword());
		        	List<Bibliografia> busqueda=Servicios.getBuscadorWeb().buscar(key.getKeyword());
		        	Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"\n\n********Fuentes a analizar:"+key.getKeyword());
		        	
		        	
		        	for(Bibliografia bibliografia:busqueda)
		        		if(!descargasMap.containsKey(bibliografia.getUrl()))
		                {
		                	/**
		                	 * Proceso que corre cuando una bibliografia(url) aparece por primera vez
		                	 */
		                	Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"(Aparicion 1)URL:"+bibliografia.getUrl());
		                	DescargasManager descarga=new DescargasManager(key,bibliografia,descargarDocumento);
		                   	descargasMap.put(bibliografia.getUrl(), descarga);
		                }
		                else
		                {
		                	/**
		                	 * Proceso que corre cuando una bibliografia(url) aparece mas de una vez
		                	 */
		                	Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"(Aparicion N)URL:"+bibliografia.getUrl());
		                	DescargasManager descarga =descargasMap.get(bibliografia.getUrl());
		                	descarga.addKey(key);
		                	descarga.incrementarFrecuencia();
		                }
		            Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"**********************Finaliza buqueda "+kIdx+"de"+totK+"con motores para:"+key.getKeyword()+"\n\n\n\n\n\n");
		            kIdx++;
		            
		            }catch(Exception errorDescKey)
		            {
		            	Log.log(errorDescKey,Log.MOD_EJECUTOR,Log.LEVEL_ERROR, Ejecutor.class,"Error en webcrawling de key");
          	        }    
		         }
		         
		     	 
		         
		         Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"\n\n\n\n\n**********************Enlaces WEB encontradas:");        
		         for(DescargasManager desc:descargasMap.values())
		        	 Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"Frecuencia: "+desc.bibliografia.getFrecuenciaAparicion()+"URL="+desc.bibliografia.getUrl());
		         Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"\n\n\n\n\n**********************************************");
		         
		         
		         
		         List<DescargasManager> descargas=new ArrayList<DescargasManager>();
		         /////Filtrar niveles de frecuencias
		         int[]frecuencias=new int [nivelFrecuencia];
		         for(int i=0;i<nivelFrecuencia;i++)frecuencias[i]=-1;
		                 
	        	 int maxFrecAnt=Integer.MAX_VALUE;
	        	 for(int frec=0;frec<nivelFrecuencia;frec++)//obtiene las n frecuencias mas altas
	        	 {
	        		 int maxFrec=0;
		        	 for(DescargasManager desc:descargasMap.values())
		        	 {	
		        		 int frecActual=desc.bibliografia.getFrecuenciaAparicion();
		        		 if(frecActual>maxFrec&&frecActual<maxFrecAnt)
		        	 	 	 maxFrec=frecActual;
		        	 }
		        	 maxFrecAnt=maxFrec;
		        	 frecuencias[frec]=maxFrec;
		         }
		         
	        	 for(Integer frec:frecuencias )
		        	 for(DescargasManager desc:descargasMap.values())
		        	 	 if(desc.bibliografia.getFrecuenciaAparicion()==frec)
		        	 	 	 descargas.add(desc);
		        	
	        	 descargasMap=null;	 	 	 

		        	 
		        	 
		         
		         
		         ///////////////////////////////////
		         
		         
		         long tiempo=ConfiguracionConexionWeb.timeoutDescarga;
		         Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"Tiempo estimado en segundos: "+tiempo/1000);
		         ProcessPool poolProcess=new ProcessPool(ConfiguracionConexionWeb.descargasParalelas, tiempo/1000);
		         for(DescargasManager d:descargas) //agrega las descargas al pool
		              	 poolProcess.addProcess(d);
		        poolProcess.ejecutar();
		         
         }else  Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_WARNING, Ejecutor.class,"Algoritmo de extraccion no encontrado..");
        	 
     	 Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"\n\n\n\n\n**********************Fuentes WEB descargados:");
         for(Fuente f:fuentes)
        	 Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"Fuente-==>Frecuencia:"+f.getFrecuenciaAparicion()+"\tKeywords:"+f.getKeys().size()+"\tURL:"+f.getRutaOrigen());
       	 Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"**********************************************");
    	}catch(Exception errorGeneral)
    	{
    		Log.log(errorGeneral,Log.MOD_EJECUTOR,Log.LEVEL_ERROR, Ejecutor.class,"Error general en proceso de webcrawling");
    	}
         return fuentes;   
    }
    private ArrayList<Fuente> buscarFuentesLocales(List<String> fuentesLocales)
    {
    	 
        ArrayList<Fuente> fuentes=new ArrayList<Fuente>();
            if(fuentesLocales!=null) 
        	for(String ruta:fuentesLocales)
             {
            	 
            	 Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"cargando fuente local:"+ruta);
            	 fuentes.add(new Fuente(new DocumentoLeido(ruta).getContenido(),"local",ruta));
             }
         
         return fuentes;  
    }
     
    public String extraerNoCitado()
    {
    	
    	return "";
    }
    

    public 	ResultAnalisis analisisTextual(SolicitudAnalisisJMS sol)
	{
		
    	ResultAnalisis analisis=null;
    	
		Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"\n\n\n*******************************Iniciando analisis");
		Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"");
		System.out.println(sol.toString());
		sol.setHora(sol.getHora()==null?(new Fechas()).horaActual:sol.getHora());
		List<ResultAnalisisFinal> result=null;
			try{
				analisis= analizar(sol.getRutaArchivoSospechoso(),sol.getCodigoAlgoritmoComparacion(),sol.getCodigoAlgoritmoExtraccion(),sol.isBusquedaWeb(), sol.getFuentesLocales(),sol.isDescargarDocumento(),sol.getNivelFrecuencia());
				if(analisis!=null)
				{
					ReporteData rdata=new ReporteData(sol,analisis.getResultadosComparacion(),analisis.getResultadosExtraccion() );
					analisis.setReporteData(rdata);
				}
				else
					Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_ERROR, Ejecutor.class,"Error en analisis Textual, resultado null..");
				
				
			}catch(Exception ex)
			{
				Log.log(ex,Log.MOD_EJECUTOR,Log.LEVEL_ERROR, Ejecutor.class,"Error al analizar..");
				
			}
		
			
			Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"\n*******************************Fin del analisis\n\n");
		
		return analisis;
	}
    
        private class DescargasManager  extends ProcesoGestionado
        {
        	private List<Keyword> keys=new ArrayList<Keyword>();
        	private Bibliografia bibliografia;
        	boolean descargarDocumento;
        	public DescargasManager(List<Keyword> keys, Bibliografia bibliografia,boolean descargarDocumento)
        	{
        		this.descargarDocumento=descargarDocumento;
        		this.keys=keys;
        		this.bibliografia=bibliografia;
        	}
        	public DescargasManager(Keyword key, Bibliografia bibliografia,boolean descargarDocumento)
        	{
        		this.descargarDocumento=descargarDocumento;
        		this.keys.add(key);
        		this.bibliografia=bibliografia;
        	}
        	/**
        	 * Agrega un keyword, esto sucede cuando una misma fuente aparece para varios keywords
        	 */
        	public void addKey(Keyword k)
        	{
        		keys.add(k);
        	}
        	/**
        	 * Metodo que incrementa el numero de veces que aparece un enlace
        	 */
        	public void incrementarFrecuencia()
        	{
        		this.bibliografia.setFrecuenciaAparicion(bibliografia.getFrecuenciaAparicion()+1);
        		
        	}
			@Override
			public void implementacion() {
				Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"Cargando fuente web: "+bibliografia.getUrl());
    			Fuente f=new Fuente();
				f.setRutaOrigen(bibliografia.getUrl());
				f.setTipo("Web");
				f.setKeys(keys);
				f.setResumen(bibliografia.getResumen());
				f.setFrecuenciaAparicion(bibliografia.getFrecuenciaAparicion());
    			if(descargarDocumento)
    			{
    				Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"Descargando fuente"+bibliografia.getUrl());
    				Pagina r=Servicios.getDownloader().descargarContenido(bibliografia.getUrl());
    				f.setContenido(r.getContenido());
    				f.setMetadatos(r.getMetadatos());
    			}
    			else
    			{
    				f.setContenido(bibliografia.getResumen());
    			}	
    			Log.log(null,Log.MOD_EJECUTOR,Log.LEVEL_MSG, Ejecutor.class,"Agregando al analisis la fuente"+bibliografia.getUrl());
    			Ejecutor.fuentes.add(f);				
			}
        	
        }

}
